package com.njcb.ams.store.page.interceptor;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;

import com.njcb.ams.pojo.bo.Head;
import com.njcb.ams.util.AmsParamUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.njcb.ams.portal.SysBaseDefine;
import com.njcb.ams.factory.comm.DataBus;
import com.njcb.ams.store.page.Page;
import com.njcb.ams.store.page.PageHandle;
import com.njcb.ams.support.exception.ExceptionUtil;
import com.njcb.ams.util.AmsIOUtils;

/**
 * @version 1.0
 * @author liuyanlong
 *
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class}),
		@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) })
public class PageInterceptor implements Interceptor {
	private static final Logger logger = LoggerFactory.getLogger(Interceptor.class);

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Page page = PageHandle.getPlatformPage();
		if (null == page || !page.isOpen()) {
			return invocation.proceed();
		}
		
		if(page.getPage() == 0 || page.getRows() == 0){
			return invocation.proceed();
		}
		
		if (invocation.getTarget() instanceof StatementHandler) {
			StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
			MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
			// 分离代理对象链(由于目标类可能被多个拦截器拦截，从而形成多次代理，通过下面的两次循环
			// 可以分离出最原始的的目标类)
			while (metaStatementHandler.hasGetter("h")) {
				Object object = metaStatementHandler.getValue("h");
				metaStatementHandler = SystemMetaObject.forObject(object);
			}
			// 分离最后一个代理对象的目标类
			while (metaStatementHandler.hasGetter("target")) {
				Object object = metaStatementHandler.getValue("target");
				metaStatementHandler = SystemMetaObject.forObject(object);
			}
			MappedStatement mappedStatement = (MappedStatement) metaStatementHandler
					.getValue("delegate.mappedStatement");
			BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
			// 分页参数作为参数对象parameterObject的一个属性
			String sql = boundSql.getSql();
			Connection connection = (Connection) invocation.getArgs()[0];
			//导出数据处理
			if (null != DataBus.getUncheckInstance()
					&& null != DataBus.getObject(Head.class)
					&& SysBaseDefine.QUERY_MODEL_EXPALL.equals(DataBus.getObject(Head.class).getModel())){
				int resultCount = getCount(sql, connection, mappedStatement, boundSql);
				if (resultCount > SysBaseDefine.MAX_EXPORT_EXCEL_NUMBER) {
					ExceptionUtil.throwAppException("导出条数不能超出[" + SysBaseDefine.MAX_EXPORT_EXCEL_NUMBER + "]");
				}
			}else{
				// 重写sql
				String pageSql = "";
				DatabaseMetaData meta = connection.getMetaData();
				if(meta.getClass().getName().contains("mysql")){
					//mysql数据库
					pageSql = buildPageSqlByMySQL(sql, page);
				}else{
					//oracle数据库
					pageSql = buildPageSqlByOracle(sql, page);
				}
				// 重写分页sql
				metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
				// 重设分页参数里的总页数等
				setPageParameter(page, getCount(sql, connection, mappedStatement, boundSql));
				// 将执行权交给下一个拦截器
			}
			//如果不是全局分页，分页一次后就关闭分页
			if(!page.isGlobal()){
				page.open(false);
			}
			//默认第一次设置分页总条数
			page.setTotal(false);
			return invocation.proceed();
		} else if (invocation.getTarget() instanceof ResultSetHandler) {
			Object result = invocation.proceed();
			//如果不是全局分页，分页一次后就关闭分页
			page.setResult((List<?>) result);
			if(!page.isGlobal()){
				page.open(false);
			}
			page.setTotal(false);
			return result;
		}
		return null;
	}

	/**
	 * @param sql
	 * @param boundSql
	 */
	private int getCount(String sql, Connection connection,MappedStatement mappedStatement, BoundSql boundSql) {
		// 记录总记录数
		String countSql = "select count(1) from (" + sql + ") ctmp ";
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		int totalCount = 0;
		try {
			ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement,
					boundSql.getParameterObject(), boundSql);
			pstmt = connection.prepareStatement(countSql);
			parameterHandler.setParameters(pstmt);
			rs = pstmt.executeQuery();
			if (rs.next()) {
				totalCount = rs.getInt(1);
			}
		} catch (SQLException e) {
			logger.error("异常待处理", e);
		} finally {
			AmsIOUtils.close(rs,pstmt);
		}
		return totalCount;
	}

	/**
	 * 只拦截这两种类型的 <br>
	 * StatementHandler <br>
	 * ResultSetHandler
	 * 
	 * @param target
	 * @return
	 */
	@Override
	public Object plugin(Object target) {
		if (target instanceof StatementHandler || target instanceof ResultSetHandler) {
			return Plugin.wrap(target, this);
		} else {
			return target;
		}
	}

	@Override
	public void setProperties(Properties properties) {

	}

	/**
	 * 修改原SQL为分页SQL
	 * 
	 * @param sql
	 * @param page
	 * @return
	 */
	private String buildPageSqlByOracle(String sql, Page page) {
		StringBuilder pageSql = new StringBuilder(200);
		pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
		pageSql.append(sql);
		pageSql.append(" ) temp where rownum <= ").append(page.getEndRow());
		pageSql.append(") where row_id > ").append(page.getStartRow());
		return pageSql.toString();
	}
	
	private String buildPageSqlByMySQL(String sql, Page page) {
		StringBuilder pageSql = new StringBuilder(200);
		pageSql.append("select temp.* from ( ");
		pageSql.append(sql);
		pageSql.append(" ) temp limit " + page.getStartRow() + "," + (page.getEndRow() - page.getStartRow()) + " ");
		return pageSql.toString();
	}

	/**
	 * 设置分页参数
	 * @param page
	 * @param totalCount
	 */
	private void setPageParameter(Page page, int totalCount) {
		int totalPage = 10;
		if(page.isSetTotal()){
			page.setTotal(totalCount);
		}
		if (page.getRows() != 0) {
			totalPage = totalCount / page.getRows() + ((totalCount % page.getRows() == 0) ? 0 : 1);
		}
		page.setPages(totalPage);
	}
}
